# 
# To change this template, choose Tools | Templates
# and open the template in the editor.
  require "timeout"
  require 'fileutils'
  def load_config
    YAML.load_file(File.join("config","configuration.yml"))
  end
  def compile(args)
    config = load_config
    file_name = args['file_name']
    file_path = args['file_path']
    language = args['language']
    just_name = file_name.match(/[^.]*/)[0] 
    errors_file = File.join(file_path,config["compile_errors"])
    case language
      when config['language']['java']
        name = File.join(file_path,file_name)
        return false unless system("javac #{name} 2>#{errors_file}")
        return "#{just_name}.class"
      when config['language']['cpp']
        name = File.join(file_path,file_name)
        return false unless system("g++ -O2 #{name} -o #{File.join(file_path,just_name)}.exe 2>#{errors_file}")
        return "#{just_name}.exe"
      when config['language']['ruby']
        return "#{file_name}"
    end
  end

  def wrong_status
    # TODO: Изтрий този метод, излишен е, според мен.
    config = load_config
    return [config['status']['wa'],config['status']['re'],config['status']['ce'],
      config['status']['tl'],config['status']['ie'],config['status']['ul'],config['status']['wo']]
  end

  def ok_status_string attempts,time
    "+#{(attempts>0)?attempts:nil}\n#{time}"
  end

  def wrong_status_string attempts,time
    "-#{(attempts>0)?attempts:nil}"
  end

  def slow_language? language
    config = load_config
    config['slow_languages'].include?(language)
  end

  # TODO: Добави още коментари към методите в този файл
  def judge_run run_id
    config = load_config
    begin
      run = Run.find_by_run_id(run_id)
      p "Run: #{run_id}"
      p "#{run.source_code}"
      run_attributes = run.attributes
      run_attributes['time_limit'] = run.problem.time_limit
      directory = run.problem.directory
      run_attributes['directory'] = directory
      run_attributes['user_name'] = run.user.login
      run = nil
      # Същинското тестване
      status = do_judge_run run_attributes
      p status
      begin
        run = Run.find_by_run_id(run_id)
      rescue Exception => e
        p "Run #{run_id} couldn't be found"
      end
      run.status = status
      error_file = File.join(directory, config['compile_errors'])

      if FileTest.exists? error_file
        p "Writing errors"
        File.open(error_file,"r") do |f|
          log = ""
          f.each do |c|
            log += c
          end
          if status == config['status']['ce']
            run.log = log
          end 
        end
        remove_file_safely error_file
      end

      return run.save
    rescue Exception => r
      p "Caught exception"
      p r
      return false
    end    
  end
  
  #require 'win32ole'
  def do_judge_run run_attributes
    config = load_config
    file_name = compile_run(run_attributes)
    file_path = run_attributes['directory']
    return config['status']['ce'] if !file_name
    full_file_name = File.join(file_path,file_name) 
    if !file_name or !FileTest.exists?(full_file_name) 
      return config['status']['ce'] 
    end
    file_name = file_name.match(/[^.]*/)[0] if run_attributes['language'] == config['language']['java']
    input_file = config['test_file']
    output_file = "#{run_attributes['user_name']}.out"
    p "#{input_file} #{output_file}"
    language = run_attributes['language']
    time_limit  = run_attributes['time_limit']
    time_limit = time_limit + time_limit/2 if slow_language?(language)
    ret_val = judge_file(file_path,file_name,input_file,output_file,time_limit,language)
    return ret_val
  end

  def compile_run run_attributes
    config = load_config
    language = run_attributes['language']
    file_path = run_attributes['directory']
    source_file = write_source(file_path,run_attributes)
    return false unless source_file
    file_name = compile('file_name' => source_file,'file_path' => file_path,'language' => language)
    remove_source(file_path,run_attributes) unless language == config['language']['ruby']
    file_name
  end

  def write_source(path,run_attributes)
    file_name = get_source_name(run_attributes)
    return false unless file_name 
    File.open(File.join(path,file_name),"w") do |f|
      f.puts run_attributes['source_code']
    end
    return file_name
  end

  def remove_source(path,run_attributes)
    file_name = get_source_name(run_attributes)
    file_path = File.join(path,file_name)
    if FileTest.exists? file_path
      remove_file_safely file_path
    end
  end
  
  def get_source_name run_attributes 
    config = load_config
    potrebitel = run_attributes['user_name']
    case run_attributes['language']
    when config['language']['java']
      temp = run_attributes['source_code'].match(/public[\s]+class[\s]+[\w]*/)
      if !temp || !temp[0].match(/[\w]*$/)
        error_file_name = File.join(run_attributes['directory'],config['compile_errors'])
        File.open(error_file_name,"w") {|f| f.puts "No public class found"}
        return nil
      end
      class_name = temp[0].match(/[\w]*$/)[0]
      return "#{class_name}.java"
    when config['language']['cpp']
      return "#{potrebitel}.cpp"
    when config['language']['ruby']
      return "#{potrebitel}.rb"
    end
  end
  
  def judge_file(file_path,file_name,input_file,output_file,time_limit,language)
    config = load_config
    begin
      timeout(time_limit) do 
        redirect = "<#{File.join(file_path,input_file)} >#{File.join(file_path,output_file)}"
        to_execute = ""
        p "Redirect: #{redirect}"
        # TODO: направи това така, че да няма изброяване.
        # Не сме вече в първи курс, полиморфизмът е хубаво нещо.
        case language
          when config['language']['java']
              to_execute = "java -cp #{file_path} #{file_name} #{redirect}"
          when config['language']['cpp']
            name = File.join(file_path,file_name)
            to_execute = "#{name} #{redirect}"
          when config['language']['ruby']
            name = File.join(file_path,file_name)
            to_execute = "ruby #{name} #{redirect}"
          else
            return config['status']['ul']
        end
        # TODO: по-качествен chmod (т.е. виж API-то, мързел, баси)
        p "To Execute: #{to_execute}"
        #p "ls -la #{file_path}"
        system("chmod 755 #{file_path}")
        
        # TODO: Process.fork
        unless system(to_execute)
          remove_file_safely File.join(file_path,output_file)
          return config['status']['re'] 
        end

        unless FileTest.exists? File.join(file_path,output_file)
          return config['status']['wo']
        end        

        if check(file_path,output_file)
          p "File was OK"
          remove_file_safely File.join(file_path,output_file)
          return config['status']['ok']
        else
          remove_file_safely File.join(file_path,output_file)
          p "File was WA"
          return config['status']['wa']
        end
      end 
    rescue TimeoutError
      #procs = WIN32OLE.connect("winmgmts:\\\\.")
      #procs.InstancesOf("win32_process").each do |p|
        #if p.parentprocessid == $$
          #`tskill #{p.processid}`
        #end
      #end
      p "TODO: FIXME: TimeoutError not implemented"
      return config['status']['tl']
    end
  end

def check(file_path,output_file)
  config = load_config
  checker_path = File.join(file_path,config['checker_name']) 
  unless FileTest.exists? checker_path
    p "Cheking with diff"
    return diff(file_path, output_file ,config['answer_file'])
  else
   p "Checking with checker"
   return system("#{checker_path} #{File.join(file_path,config['test_file'])} #{File.join(file_path,config['answer_file'])} #{File.join(file_path,output_file)}")
  end
end

def remove_file_safely file_name
  while FileTest.exists? file_name
    begin 
      FileUtils.rm file_name
    rescue Exception
    end
  end
end
  
def diff file_path, file1, file2
  config = load_config
=begin
  "TODO: Направи този diff да не е съшит с бели конци"
  "Comparing #{file1} and #{file2}"
  "Направи го и да може да връща Presentation Error"
  "Това е ужасно, оправи си го."
=end
  diff_output = `diff #{File.join(file_path, file1)} #{File.join(file_path,file2)}`
  if diff_output != ""
    p "Files are not the same, writing to errors log"
    File.open(File.join(file_path, config["compile_errors"]),"w+").puts(diff_output)
    system ('diff #{File.join(file_path, file1)} #{File.join(file_path,file2)} > #{File.join(file_path, config["compile_errors"])}')
    return false
  else
    return true
  end
end


def seek_and_judge
  while true do
    config = load_config
    judged_statuses = config['status'].map{|k,v| v}
    r = Run.find(:first,:conditions => ["status NOT IN(?)",judged_statuses])
    if r.nil?
      sleep(3)
    else 
      p "Loading a file, run_id = #{r.run_id}"
      unless judge_run(r.run_id)
        r.status = config['status']['ie']
        r.save
      end
    end
  end
end


desc "Starts the judge process"
task :judge => :environment do
  seek_and_judge
end

desc "Judges a particular run"
task :judge_run, [:given_id] => :environment do |t,args|
  judge_run(args.given_id)
end

